<?php

/** Yz_String: the basic datatype class, includes transparent multibyte string management.
*   Basic function names are borrowed from JavaScript.
*   There are also some extensions.
*
*   Inherited methods from TClass:
*     - getClass
  CHANGELOG


*/

class Yz_String extends Yz_Type
{

    const FILTER_INTEGER = '~^-?\d+$~';
    const FILTER_FLOAT = '~^-?\d*.?\d+$~';
    const FILTER_WORD = '~^\w+$~';
    const FILTER_LABEL = '~^[a-z_][0-9a-z_]*$~';

    /** The value of the string: assert a UTF-8 string
    *   @var string
    */
    protected $value;

    protected static $expects = array(
        'test'                => 'string',
        'testNotEmpty'        => 'non-empty string',
        'testNotSpace'        => 'non-all-space string',
        'testWord'            => 'string of word characters',
        'testLabel'           => 'valid identifier',
    );


    /** valueOf(): reduce the value to string
    *       Objects which have toString() do not count
    *   @param scalar value
    *   @return string|null
    */
    public static function valueOf( $value )
    {
        switch( true ) {
            case is_bool( $value ):
                $value = $value ? "true" : "false";
                break;
            case is_scalar($value): // "null" is not considered scalar
                $value = "$value";
                break;
            default:
                $value = null;
        } // switch

        return $value;
    } // valueOf


    public static function testNotEmpty( $value, $context=null )
    {
        return static::doTest( $value, $context, 'testNotEmpty', function( $val ) {
            return $val !== '';
        } );
    }


    public static function testNotSpace( $value, $context=null )
    {
        return static::doTest( $value, $context, 'testNotSpace', function( $val ) {
            return trim($val) !== '';
        } );
    }



    public static function testWord( $value, $context=null )
    {
        return static::doTest( $value, $context, 'testWord', function( $val ) {
            return preg_match( Yz_String::FILTER_WORD, $val );
        } );
    }


    public static function testLabel( $value, $context=null )
    {
        return static::doTest( $value, $context, 'testWord', function( $val ) {
            return preg_match( Yz_String::FILTER_LABEL, $val );
        } );
    }


    /** encode <>"'&\
    *   skip already encoded entities
    *   before replace, delete all non-printable chars (those before space - range 0x00:0x1F)
    *
    */
    public static function toHTML( $value )
    {

        $value = static::valueOf( $value );

        debug( $value );

        if( null === $value || "" === $value ) {
            return "";
        }
        $regexp = <<<cc
~([<>"'\\\\])|(&(?!(#x[a-f0-9]{1,4})|(#\d{1,5})|([a-z]{1,6});))~i
cc;
        $src = preg_replace( '~[\x00-\x09\x0B-\x1F]~', ' ', $value );
        $dest = preg_replace_callback(
                $regexp,
                function( $matches ) { // PHP53 anonymous function!
                    $c = ord($matches[0]);
                    return "&#$c;";
                },
                $src );
        return $dest;
    } // toHTML



    /**
    *
    *
    */
    public static function fromHTML()
    {
    }


    /** isTrue() :
    *
    * @return bool/null
    */
    public static function isTrue( $value=null )
    {

        $value = static::valueOf( $value );

        if ( null === $value ) {
            return null;
        }
/*
 * tests showed this 2x slower than switch
        $r = preg_match("~^(true|yes|on|enabled)|(false|no|off|disabled)$~i", $value, $m);
        if (! $r) {
            return null;
        }
        if (isset($m[2])) {
            return false;
        }
        return true;
*/
        // do not use mb_
        switch( strtolower($value) ) {
          case "on":
          case "true":
          case "enabled":
          case "yes":
            $r = true;
            break;
          case "off":
          case "false":
          case "disabled":
          case "no":
            $r = false;
            break;
          default:
            $r = $value;
        } // switch
        return $r;
    } // isTrue



    /**
    *   @param string parameterized_string
    *   @param THash vars
    *   @return string
    */
    public static function replaceVars( $str, $vars )
    {
        $str = Yz_String::test( $str, 'Parameterized string' );
        $vars = new Yz_Hash($vars);
        foreach ( $vars as $k => $v ) {
            if (null !== $v) {
                $v = Yz_String::test( $v, 'Parameter' );
            }
            $str = str_ireplace( '%{' .$k .'}', $v, $str );
        }

        return $str;
    } // replaceVars


    /** convert a-long-dashed-string to aLongDashedString
    *   @param string
    *   @param bool ucfirst
    *   @return string
    */
    public static function toCamelCase( $str, $ucfirst=false )
    {
        $str = Yz_String::test( $str );
        $str = preg_replace( '/[_-]([a-z])/e', 'strtoupper(\'$1\')', $str );
        true === $ucfirst
            and $str = ucfirst( $str );

        return $str;
    } // toCamelCase


    /** validate a string as an identifier
     *  and convert a-long-dashed-string to aLongDashedString
     *  @param string
     *  @return string
     */
    public static function identifier( $str )
    {
        if (! is_string($str) || '' === $str) {
            throw new Yz_Exception_InvalidArgument();
        }
        $str = preg_replace_callback( '/[_-]([a-z])/',
            function($args) {return strtoupper($args[1]); },
            $str
            );
        if (! preg_match('~[[:alpha:]][[:alnum:]]*~', $str)) {
            throw new Yz_Exception_InvalidArgument();
        }

        return $str;
    } // identifier


} // class

